﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;

namespace Simp.Model.CoreExpressions.Sequences
{
	/// <summary>Sequence of explicit elements.</summary>
	/// <remarks>The implementation holds the number of elements.  The elements themselves are passed in parameters.</remarks>
	[ExpressionName(ExplicitSequenceExpressionName)]
	[DebuggerDisplay("{ExpressionHelper.Serialize(this)}")]
	public class ExplicitSequenceExpression : IParameterPolicyExpression, ISequenceExpression, IExpressionSerializer
	{
		#region Inner Types
		private class ExplicitSequenceAccessor : ISequenceAccessor
		{
			private readonly IExpression[] _elementList;

			public ExplicitSequenceAccessor(params IExpression[] elementList)
			{
				_elementList = elementList;
			}

			#region ISequenceAccessor Members
			int ISequenceAccessor.Count
			{
				get { return _elementList.Length; }
			}

			IExpression ISequenceAccessor.this[int index]
			{
				get { return _elementList[index]; }
			}
			#endregion

			#region IEnumerable<IExpression> Members
			IEnumerator<IExpression> IEnumerable<IExpression>.GetEnumerator()
			{
				foreach (IExpression expression in _elementList)
				{
					yield return expression;
				}
			}
			#endregion

			#region IEnumerable Members
			IEnumerator IEnumerable.GetEnumerator()
			{
				return _elementList.GetEnumerator();
			}
			#endregion
		}
		#endregion

		/// <summary>Name of the expression.</summary>
		public const string ExplicitSequenceExpressionName = "ExplicitSequence";

		private readonly IParameterBinding[] _parameterBindings;

		/// <summary>Construct a sequence with its length (the elements are expression parameters).</summary>
		/// <param name="parameterCount"></param>
		public ExplicitSequenceExpression(int parameterCount)
		{
			if (parameterCount < 0)
			{
				throw new ArgumentException("Must be greater or equal to zero", "parameterCount");
			}

			_parameterBindings = ExpressionHelper.CreateParameterBinding(parameterCount);
		}

		#region IParameterPolicyExpression Members
		IParameterBinding[] IParameterPolicyExpression.ParameterBindings
		{
			get { return _parameterBindings; }
		}
		#endregion

		#region ISequenceExpression Members
		ISequenceAccessor ISequenceExpression.GetAccessor(params IExpression[] parameters)
		{
			if (parameters == null)
			{
				throw new ArgumentNullException("parameters");
			}
			if (parameters.Length != _parameterBindings.Length)
			{
				throw new ArgumentException("Wrong # of parameters", "parameters");
			}

			return new ExplicitSequenceAccessor(parameters);
		}
		#endregion

		#region IExpressionSerializer Members
		void IExpressionSerializer.Serialize(AppendOnlyStringBuilder builder, params IExpression[] parameters)
		{
			if (builder == null)
			{
				throw new ArgumentNullException("builder");
			}
			if (parameters == null)
			{
				throw new ArgumentNullException("parameters");
			}

			builder.Append("{");
			for (int i = 0; i != parameters.Length; ++i)
			{
				IExpression expression = parameters[i];

				ExpressionHelper.Serialize(builder, expression);
				if (i != parameters.Length - 1)
				{
					builder.Append(", ");
				}
			}
			builder.Append("}");
		}
		#endregion
	}
}